package com.kyle.pagingdemo.paging

import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.kyle.pagingdemo.IApi
import com.kyle.pagingdemo.data.model.Page
import com.kyle.pagingdemo.data.model.Post
import com.kyle.pagingdemo.room.ArticleDatabase
import retrofit2.HttpException
import java.io.IOException
import java.io.InvalidObjectException

/**
 * author: liliang
 * date: 1/6/21 9:11 PM
 * desc:
 */

@OptIn(ExperimentalPagingApi::class)
class ArticleRemoteMediator(private val api: IApi, private val db: ArticleDatabase) :
    RemoteMediator<Int, Post>() {

    private val articleDao = db.articleDao()

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, Post>
    ): MediatorResult {

        val page = when (loadType) {
            LoadType.REFRESH -> {
                val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
                remoteKeys?.nextKey?.minus(1) ?: 0
            }
            LoadType.PREPEND -> {
                return MediatorResult.Success(
                    endOfPaginationReached = true
                )
            }
            LoadType.APPEND -> {
                val remoteKeys = getRemoteKeyForLastItem(state)
                if (remoteKeys?.nextKey == null) {
                    throw InvalidObjectException("Remote key should not be null for $loadType")
                }
                remoteKeys.nextKey
            }
        }

        var localArticleSize = 0 //本地数据库数据大小
        db.withTransaction {
            localArticleSize = articleDao.getLocalPostSize()
        }
        try {
            val post = api.getPost(page).posts
            val endOfPaginationReached = post.isEmpty()
            db.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    articleDao.clearPage()
                    articleDao.clearPost()
                }
                val prevKey = if (page == 0) null else page - 1
                val nextKey = if (endOfPaginationReached) null else page + 1
                val keys = post.map {
                    Page(
                        id = it.id,
                        prevKey = prevKey,
                        nextKey = nextKey
                    )
                }
                articleDao.insertPages(keys)
                articleDao.insertPosts(post)
            }
            return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
        } catch (exception: IOException) {
            return if (loadType == LoadType.REFRESH) {
                if (localArticleSize > 0) {
                    MediatorResult.Success(true)
                } else {
                    MediatorResult.Error(exception)
                }
            } else {
                MediatorResult.Error(exception)
            }
        } catch (exception: HttpException) {
            return if (loadType == LoadType.REFRESH) {
                if (localArticleSize > 0) {
                    MediatorResult.Success(true)
                } else {
                    MediatorResult.Error(exception)
                }
            } else {
                MediatorResult.Error(exception)
            }
        }

    }

    private fun getRemoteKeyForLastItem(state: PagingState<Int, Post>): Page? {
        return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
            ?.let { repo ->
                articleDao.getPageById(repo.id)
            }
    }

    private fun getRemoteKeyClosestToCurrentPosition(state: PagingState<Int, Post>): Page? {
        return state.anchorPosition?.let { position ->
            state.closestItemToPosition(position)?.id?.let { repoId ->
                articleDao.getPageById(repoId)
            }
        }

    }
}